﻿/*
 * Please leave this Copyright notice in your code if you use it
 * Written by Decebal Mihailescu [http://www.codeproject.com/script/articles/list_articles.asp?userid=634640]
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonUtils;

namespace SubTitleConvertors
{

    using System.ComponentModel.Composition;
    using System.IO;
    using System.Text.RegularExpressions;

    [Export(typeof(ITextImport))]
    [ExportMetadata("SpecialFormat", true)]
    [ExportMetadata("Description", "Srt subtitle files")]
    [ExportMetadata("FileExtension", "*.srt")]
    [ExportMetadata("ContextMenuText", "&Translate")]
    public class SrtFileReader : ITextImport, ITextManipulation
    {

        #region Imports
        [Import("WebSite")]
        public string _strWebSite;
        [Import("SrtLogoFormat")]
        public string _LogoFormat;
        #endregion

        #region ITextImport Members

        public string GetAllTextFromFile(string fileName, out Encoding encoding)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException(null, "fileName");
            }
            if (!File.Exists(fileName))
            {
                throw new ArgumentException("File '" + fileName + "' is not found");
            }
            string str = string.Empty;
            try
            {
                var arg = new ProgressEventArgs();
                arg.Description = "Opening Srt file";
                arg.Progress = 0;
                if (ProgressChanged != null && ProgressChanged.GetInvocationList().Length > 0)
                    ProgressChanged(this, arg);
                Encoding encoding_guessed = EncodingEx.GetFileEncoding(fileName);
                string txt = null;
                using (StreamReader reader = new StreamReader(fileName, true))//GetEncoding(0x4e3)))
                {

                    txt = reader.ReadToEnd();
                    encoding = reader.CurrentEncoding;
                }
                //txt = txt.Replace("\r\r", "\r");
                txt = txt.FixEoL();

                List<string[]> lst = ((ITextManipulation)this).GetSplitedText(txt) as List<string[]>;
                //try the encoding_guessed if failed
                if (lst.Count <= 0 && encoding_guessed != encoding)
                {
                    using (StreamReader reader = new StreamReader(fileName, encoding_guessed))
                    {
                        txt = reader.ReadToEnd();
                        encoding = reader.CurrentEncoding;
                    }
                    txt = txt.FixEoL();
                }
                lst = ((ITextManipulation)this).GetSplitedText(txt) as List<string[]>;
                int len = txt.Length;
                //remove first bad tites with index == 0
                while (_zeroTitle.IsMatch(lst[0][0]))
                {
                    len -= (lst[0][0].Length + lst[0][1].Length);
                    lst.RemoveAt(0);
                }
                //if (lst[0][1].IndexOf(MEFRepository._strWebSite, StringComparison.InvariantCultureIgnoreCase) == -1)
                if (lst[0][1].IndexOf(_strWebSite, StringComparison.InvariantCultureIgnoreCase) == -1)
                    len += AddFLTTitle(lst);
                else if (lst[0][1].IndexOf(_strWebSite, StringComparison.InvariantCultureIgnoreCase) > 0)
                {
                    lst[0][1] = _strWebSite + "\r\n\r\n";
                    len += _strWebSite.Length + 2;

                }
                StringBuilder sb = new StringBuilder(len + 10);
                lst.ForEach(sub => { sb.Append(sub[0]); sb.Append(sub[1]); });
                txt = sb.ToString();
                sb.Clear();
                Match m = Regex.Match(lst[0][0], @"^[ \t]*[0]{1}[ \t]*\r\n", RegexOptions.Multiline);
                bool bNeedsIncr = m.Success;
                if (bNeedsIncr)
                    txt = Regex.Replace(txt, @"((^[ \t]*[0-9]+[ \t]*\r\n)|(\r\n\r\n[ \t]*[0-9]+[ \t]*\r\n))", MatchEval);
                return txt;

            }
            catch (Exception exception)
            {
                throw new Exception("Error during text extraction from Text-file: " + fileName + Environment.NewLine + exception.Message);
            }

        }


        private static TimeSpan TimeSpanFromString(string strTime)
        {
            string first = strTime.Replace(',', '.');
            IFormatProvider culture = new System.Globalization.CultureInfo("en-US", false);
            return TimeSpan.Parse(string.Format("0.{0}", first), culture);//ts;
        }

        private static string StringFromTimeSpan(TimeSpan time)
        {
            return string.Format("{0:00}:{1:00}:{2:00},{3:000}", time.Hours, time.Minutes, time.Seconds, time.Milliseconds);
        }

        readonly Regex _getTimes = new Regex(@"(?<Begin>(?:[0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3})[ \t]*-->[ \t]*(?<End>(?:[0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3})[ \t]*\r\n", RegexOptions.Compiled);
        private int AddFLTTitle(List<string[]> lst)
        {
            try
            {

                Match m = _times.Match(lst[0][0]);
                string firstTimes = lst[0][0].Substring(m.Index, m.Length);
                var g = m.Groups[0];
                if (!string.IsNullOrEmpty(firstTimes))
                {
                    string[] myentrtry = new string[2];

                    Match match = _getTimes.Match(firstTimes);

                    TimeSpan ts = TimeSpanFromString(match.Groups["Begin"].ToString());
                    if (ts.TotalSeconds < 30)
                    {
                        TimeSpan ts0 = TimeSpan.FromTicks(ts.Ticks / 5);
                        TimeSpan ts1 = TimeSpan.FromTicks(ts.Ticks / 5 * 4);
                        myentrtry[0] = string.Format("0\r\n{0}-->{1}\r\n", StringFromTimeSpan(ts0), StringFromTimeSpan(ts1));
                    }
                    else
                    {
                        TimeSpan ts0 = TimeSpan.FromTicks(ts.Ticks / 3);
                        TimeSpan ts1 = TimeSpan.FromTicks(ts.Ticks / 3 * 2);
                        myentrtry[0] = string.Format("0\r\n{0}-->{1}\r\n", StringFromTimeSpan(ts0), StringFromTimeSpan(ts1));
                    }
                    myentrtry[1] = _strWebSite + "\r\n\r\n";
                    lst.Insert(0, myentrtry);
                    return myentrtry[0].Length + myentrtry[1].Length;
                }

            }
            catch (Exception ex)
            {

                return 0;
            }


            return 100;
        }
        public event EventHandler ProgressChanged;

        static string MatchEval(Match m)
        {
            // Get the matched string.
            string x = m.ToString();

            try
            {
                if (x.StartsWith("\r\n\r\n"))
                {
                    string tmp = x.Trim();
                    int no = int.Parse(tmp);
                    no++;
                    return string.Format("\r\n\r\n{0}\r\n", no);
                }
                else
                {
                    string tmp = x.Trim();
                    int no = int.Parse(tmp);
                    no++;
                    return string.Format("{0}\r\n", no);
                }
            }
            catch
            {
                return x;
            }

        }


        private bool ShowProgress(string desc, int progress)
        {
            var arg = new ProgressEventArgs();
            arg.Description = desc;
            arg.Progress = progress;
            if (ProgressChanged != null && ProgressChanged.GetInvocationList().Length > 0)
            {
                ProgressChanged(this, arg);
                return true;
            }
            else
                return false;

        }

        private static string[] FragmentFromMatch(Match match)
        {
            string[] sub = new string[2];
            sub[0] = string.Format("{0}\r\n{1}-->{2}\r\n", match.Groups["Index"], match.Groups["Begin"], match.Groups["End"]);
            sub[1] = match.Groups["Text"].Value + "\r\n";
            return sub;
        }
        private static string[] FragmentFromMatch2(Match match)
        {
            string[] sub = null;
            
            StringBuilder sb = new StringBuilder(200);
            try
            {
                if (match.Groups["Index"].Captures.Count == 0)
                    return null;
                sub = new string[2];
                //ToString() works too instead of Value
                sub[0] = string.Format("{0}\r\n{1}-->{2}\r\n", match.Groups["Index"].Value, match.Groups["Begin"].Value, match.Groups["End"]);
                string grp = null;
                if (match.Groups["Text0"].Captures.Count > 0)
                {
                    grp = match.Groups["Text0"].Captures[0].Value;
                    if (!string.IsNullOrEmpty(grp))
                        sb.Append(grp);
                }
                if (match.Groups["Text1"].Captures.Count > 0)
                {
                    grp = match.Groups["Text1"].Captures[0].Value;
                    grp = grp.TrimEnd(new char[] {'\r','\n'});//there is some issue in the reg ex, fixing it
                    if (!string.IsNullOrEmpty(grp))
                        sb.Append(grp);
                         sb.Append("\r\n");//the second line has a \n even when empty
                }
                sb.Append("\r\n");
                sub[1] = sb.ToString();
            }
            catch (Exception ex)
            {
            }
            finally
            {
                sb.Clear();
            }
            //sub[1] = match.Groups["Text"].Value + "\r\n";
            return sub;
        }
        #endregion
        private readonly Regex _fragmentsRegex2 =
            new Regex(@"[ \t]*(?<Index>\d+)[ \t]*\r\n(?<Begin>(?:\d{2}[\:]){2}\d{2}[,]\d{3})[ \t]*-->[ \t]*(?<End>(?:\d{2}[\:]){2}\d{2}[,]\d{3})[ \t]*\r\n(?<Text0>(?:.+\r\n))(?<Text1>(?:.+(?:\r\n|$))|\r\n|$)(?=(?:\r\n)*\d+\r\n(?:\d{2}[\:]){2}\d{2}[,]\d{3}|(?:\r\n)*$)", RegexOptions.Compiled | RegexOptions.Multiline);
        private readonly Regex _fragmentsRegex = 
             new Regex(@"[ \t]*(?<Index>[0-9]+)[ \t]*\r\n(?<Begin>(?:[0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3})[ \t]*-->[ \t]*(?<End>(?:[0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3})[ \t]*\r\n(?<Text>(.+\r\n)+)(\r\n)*", RegexOptions.Compiled | RegexOptions.Multiline);
        Regex _zeroTitle =
            new Regex(@"^[ \t]*[0]+[ \t]*\r\n[ \t]*([0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3}[ \t]*-->[ \t]*([0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3}[ \t]*\r\n", RegexOptions.Compiled);
        Regex _times =
            new Regex(@"[ \t]*([0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3}[ \t]*-->[ \t]*([0-9]{2}[\:]){2}[0-9]{2}[,][0-9]{3}[ \t]*\r\n", RegexOptions.Compiled | RegexOptions.Multiline);

        #region ITextManipulation Members

        string ITextManipulation.PreTranslation(string frg)
        {
            return frg.Replace("\r\n", "<br>");
        }

        string ITextManipulation.PostTranslation(string frg)
        {
            return frg + "\r\n";
        }
        IList<string[]> ITextManipulation.GetSplitedText(string text)
        {
            string str = string.Empty;
            try
            {
                var arg = ShowProgress("Opening srt file", 0);

                MatchCollection matches = _fragmentsRegex2.Matches(text);
                //MatchCollection matches = _fragmentsRegex.Matches(text);
                //List<string[]> fragments = matches.OfType<Match>().Select((Match match, int i) => { if (i != 0 && i % 200 == 0) ShowProgress("...", i * 100 / matches.Count); return FragmentFromMatch(match); }).ToList();
                List<string[]> fragments = matches.OfType<Match>().Select((Match match, int i) => { if (i != 0 && i % 200 == 0) ShowProgress("...", i * 100 / matches.Count); return FragmentFromMatch2(match); }).Where(sub => sub != null).ToList();
                ShowProgress("Srt file Opened", 100);
                return fragments;
            }
            catch (Exception exception)
            {
                //throw new Exception("Error during text extraction from Text-file: " + fileName + Environment.NewLine + exception.Message);
                return null;
            }
        }

        string ITextManipulation.LogoFormat
        {
            // get { return MEFRepository._strSrtLogoFormat  }
            get { return _LogoFormat; }
        }

        #endregion
    }
}
